<template>
    <div class="scanCode">
        <div class="container">
            <div class="qrcode">
                <div id="reader"></div>
            </div>
        </div>
        <div class="btn">
            <div class="left-back">
                <van-icon name="arrow-left" @click="clickBack" />
            </div>
            <div class="right-file">
                <van-uploader 
                    v-model="fileList" 
                    :preview-image="false" 
                    :after-read="dealSelectFiles"
                >
                    <van-icon name="photo-o" />
                </van-uploader>
            </div>
        </div>
    </div>
</template>

<script setup>
// 1. 导入依赖（显式声明，依赖关系更清晰）
import { ref, onMounted, onUnmounted } from 'vue'
import { Html5Qrcode } from 'html5-qrcode'
import { ElNotification } from 'element-plus'
import jsQR from 'jsqr'
 

// 2. 定义组件事件（直接通过 defineEmits 声明，无需中转）
const emit = defineEmits(['onScanSuccess', 'onScanError', 'onBack'])

// 3. 响应式状态管理（用 ref 替代 reactive+toRefs，更简洁）
const html5QrCode = ref(null) // 扫码实例
const fileList = ref([])      // 上传文件列表
const isScanning = ref(false) // 扫描状态标记

// 4. 扫描成功处理（直接触发 emit，无需额外事件发射器）
const handleScanSuccess = (result) => {
    emit('onScanSuccess', result)
    ElNotification({
        title: '扫描成功',
        message: result,
        type: 'success',
        duration: 3000,
    })
}

// 5. 扫描错误处理（统一错误提示和事件触发）
const handleScanError = (error, message = '扫描失败，请重试') => {
    emit('onScanError', { error, message })
    ElNotification({
        title: 'QR错误提示',
        message: message,
        type: 'error',
        duration: 3000,
    })
}

// 6. 启动扫描（简化状态判断，直接操作 ref 变量）
const startScan = () => {
    if (!html5QrCode.value) return

    isScanning.value = true
    html5QrCode.value
        .start(
            { facingMode: 'environment' }, // 使用后置摄像头
            { fps: 1, qrbox: { width: 250, height: 250 } }, // 扫描配置
            (decodedText) => { // 成功回调
                stopScan()
                handleScanSuccess(decodedText)
            }
        )
        .catch((err) => { // 错误处理
            console.error('扫码错误:', err)
            // 错误信息映射（保持原有逻辑）
            const errorMsgMap = {
                'NotAllowedError': '您需要授予相机访问权限！',
                'NotFoundError': '设备上没有可用摄像头！',
                'NotSupportedError': '摄像头仅支持 https/localhost 环境！',
                'NotReadableError': '相机正在被其他应用占用！',
                'OverconstrainedError': '摄像头配置不支持当前需求！',
                'StreamApiNotSupportedError': '浏览器不支持摄像头流API！'
            }
            const errMsg = typeof err === 'string' 
                ? '二维码识别失败！' 
                : errorMsgMap[err.name] || '扫码过程中发生错误'
            
            handleScanError(err, errMsg)
        })
}

// 7. 获取摄像头并初始化扫描（简化 Promise 逻辑）
const initCamera = () => {
    Html5Qrcode.getCameras()
        .then((devices) => {
            if (devices?.length) {
                html5QrCode.value = new Html5Qrcode('reader') // 绑定 DOM 容器
                startScan()
            } else {
                handleScanError(null, '未检测到可用摄像头')
            }
        })
        .catch((err) => {
            console.error('获取摄像头失败:', err)
            ElNotification({
                title: '权限错误',
                message: '摄像头无访问权限！',
                type: 'error',
                duration: 3000
            })
            handleScanError(err, '获取摄像头权限失败')
        })
}

// 8. 停止扫描（优化状态重置逻辑）
const stopScan = () => {
    if (!html5QrCode.value || !isScanning.value) return

    html5QrCode.value
        .stop()
        .then(() => {
            console.log('扫描已停止')
            isScanning.value = false
        })
        .catch((err) => {
            console.error('停止扫描失败:', err)
            handleScanError(err, '停止扫码失败')
            isScanning.value = false
        })
}

// 9. 图片文件解码（基于 jsQR，保持原有逻辑）
const dealSelectFiles = () => {
    try {
        const selectedFile = fileList.value[0]?.file
        if (!selectedFile) return

        const fileReader = new FileReader()
        fileReader.onload = (e) => {
            const img = new Image()
            img.onload = () => {
                // 创建 canvas 处理图像
                const canvas = document.createElement('canvas')
                const ctx = canvas.getContext('2d')
                canvas.width = img.width
                canvas.height = img.height
                ctx.drawImage(img, 0, 0, canvas.width, canvas.height)

                // 用 jsQR 解码图像数据
                const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                const decodedResult = jsQR(imageData.data, imageData.width, imageData.height, {
                    inversionAttempts: 'dontInvert' // 不反转颜色（提高识别稳定性）
                })

                if (decodedResult) {
                    handleScanSuccess(decodedResult.data)
                } else {
                    handleScanError(null, '未识别到二维码')
                }
                fileList.value = [] // 清空文件列表
            }

            img.onerror = () => {
                throw new Error('图片加载失败')
            }
            img.src = e.target.result // 加载图片数据
        }

        fileReader.onerror = () => {
            throw new Error('文件读取失败')
        }
        fileReader.readAsDataURL(selectedFile) // 读取文件为 Base64
    } catch (error) {
        console.error('图片识别失败:', error)
        handleScanError(error, '图片识别失败，请确保包含清晰二维码')
        fileList.value = []
    }
}

// 10. 返回按钮逻辑（停止扫描后触发返回事件）
const clickBack = () => {
    stopScan()
    emit('onBack')
}

// 11. 生命周期钩子（简化挂载/卸载逻辑）
onMounted(() => {
    initCamera() // 组件挂载后初始化摄像头
})

onUnmounted(() => {
    if (isScanning.value) {
        stopScan() // 组件卸载前停止扫描，避免内存泄漏
    }
})
</script>

<style lang="scss" scoped>
.scanCode {
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: rgba(0, 0, 0);
}

.container {
    height: 90vh;
    position: relative;
    width: 100%;
}

.qrcode {
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}

#reader {
    width: 100%;
    max-width: 400px;
}

.btn {
    flex: 1;
    padding: 3vw;
    display: flex;
    justify-content: space-around;
    color: #fff;
    font-size: 8vw;
    align-items: flex-start;
}

.left-back,
.right-file {
    cursor: pointer;
    transition: transform 0.2s ease;

    &:active {
        transform: scale(0.9); // 点击反馈
    }
}
</style>